package com.leyou.item.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.Mapper.SkuMapper;
import com.leyou.item.Mapper.SpuDetailMapper;
import com.leyou.item.Mapper.SpuMapper;
import com.leyou.item.pojo.*;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
// 继承ServiceImpl可以批量添加Sku
public class GoodsService extends ServiceImpl<SkuMapper, Sku> {

    @Autowired
    private SpuMapper spuMapper;
    @Autowired
    private SpuDetailMapper spuDetailMapper;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private BrandService brandService;
    @Autowired
    private CategoryService categoryService;

    public PageResult<SpuDTO> spuPageQuery(Integer page, Integer rows, String key, Boolean saleable) {

        //1.封装条件
        //1.1 封装分页条件
        IPage<Spu> iPage = new Page<>(page, rows);

        //1.2 封装查询条件
        QueryWrapper<Spu> query = Wrappers.query();

        //key查询条件
        //目前的sql: name like '%三星%' or sub_title like '%三星%'
        //要求的sql: ( name like '%三星%' or sub_title like '%三星%' )
        if (StringUtils.isNotEmpty(key)) {
            /**
             * 注意：queryWrapper.and()方法可以把需要的多个条件作为一个整体和其他条件参与运算（给多个条件加个括号）
             */
            query.and(
                    // query.and(里面要求是一个接口的实现类，这时候就要用到lambda表达式，lambda表达式的作用就是对接口实现类的书写方式进行简化)
                    /** query.and的源码用的就是Nested接口，这里要写这个接口的实现类，不用lambda表达式时，应该是这样的：
                     * query.and(new Nested(){
                     *  public QueryWrapper query(QueryWrapper i{
                     *      i.like("name",key)
                     *      .or()
                     *      .like("sub_title",key)
                     *  }
                     * });
                     */
                    i -> i.like("name", key)
                            .or()
                            .like("sub_title", key)
            );
        }
        //saleable查询条件
        //sql:saleable = true
        if (saleable != null) {
            query.eq("saleable", saleable);
        }
        // 实际sql语句: select * from tb_spu where (name like '%三星%' or sub_title like '%三星%' and saleable = true)
        //2.执行查询，获取结果
        // 把封装好的分页条件和查询条件放入selectPage
        iPage = spuMapper.selectPage(iPage, query);

        //3.处理结果，返回结果
        //取出所有Spu对象放入列表中
        List<Spu> spuList = iPage.getRecords();
        //数据拷贝
        List<SpuDTO> spuDTOList = BeanHelper.copyWithCollection(spuList, SpuDTO.class);
        //封装SpuDTO中的品牌名称和分类名称
        getCategoryNameAndBrandName(spuDTOList);

        //封装PageResult,获取所有的spu表的数据
        PageResult<SpuDTO> pageResult = new PageResult<>(iPage.getTotal(), iPage.getPages(), spuDTOList);

        return pageResult;
    }

    // 获取spuDTO中的CategoryName和brandName的方法
    public void getCategoryNameAndBrandName(List<SpuDTO> spuDTOList) {
        //遍历spuDTOList集合,找到集合中的brandId和cid1,cid2,cid3根据brandId获取品牌的名称，
        // 根据3个cid拼接成一个分类集合，再将此分类集合变成一个用“/”分割的字符串（用流式编程操作List集合）
        spuDTOList.forEach(spuDTO -> {
            //品牌名称
            Brand brand = brandService.findBrandById(spuDTO.getBrandId());
            spuDTO.setBrandName(brand.getName());

            //分类名称
            List<Category> categoryList = categoryService
                    .findCategoriesByIds(Arrays.asList(spuDTO.getCid1(), spuDTO.getCid2(), spuDTO.getCid3()));
            // Arrays.asList可以把可变参或者数组变成一个集合
            //手机通讯/手机/手机

            /**
             * stream流式编程
             *   1）作用：简化集合的操作的语法
             *   2）核心方法：
             *      2.1 stream(): 把集合转换为流（流式编程的第一步）
             *      2.2 map(): 对集合的每个元素进行处理（例如 取出元素的某个属性，打印属性名，对属性值进行算术运算 等等）
             *                 map方法可以执行多次
             *
             *            map()方法支持lambda表达式
             *               写法一： map(c->c.getName())
             *               写法二： map(Category::getName)  推荐写法
             *
             *      2.3 collect(): 对map方法处理后的结果进行归总（归集） （例如： 把每个对象的属性名称再次封装成一个List集合）
             *                collet(Collectors.toList())  将map方法每个结果收集成新的List集合
             *                collet(Collectors.toSet())  将map方法每个结果收集成新的Set集合
             *                collet(Collectors.toMap())  将map方法每个结果收集成新的Map集合
             *                collet(Collectors.join("分隔符"))  将map方法每个结果收集成以指定分隔符拼接成的字符串
             *                .......
             */
            String categoryName = categoryList.stream()
                    .map(Category::getName)// 左边是类型，右边是获取方法,c -> c.getName
                    .collect(Collectors.joining("/"));

            spuDTO.setCategoryName(categoryName);
        });
    }

    public void saveGoods(SpuDTO spuDTO) {

        try {
            //1.保存spu表
            //拷贝数据
            Spu spu = BeanHelper.copyProperties(spuDTO, Spu.class);
            //默认下架
            spu.setSaleable(false);
            spuMapper.insert(spu);

            //2.保存spu_detail表
            SpuDetail spuDetail = spuDTO.getSpuDetail();
            //设置spuId
            spuDetail.setSpuId(spu.getId());
            spuDetailMapper.insert(spuDetail);

            //3.保存sku表
            List<Sku> skus = spuDTO.getSkus();
            skus.forEach(sku -> {
                sku.setSpuId(spu.getId());

            });
            //myBatis-plus批量添加
            // GoodsService继承了ServiceImpl<SkuMapper, Sku>类，才有批量添加的方法
            this.saveBatch(skus);
        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.INSERT_OPERATION_FAIL);
        }

    }

    @Autowired
    private AmqpTemplate amqpTemplate;

    public void updateSaleable(Long id, Boolean saleable) {

        try {
            Spu spu = new Spu();
            spu.setId(id);
            spu.setSaleable(saleable);
            //updateById:底层是动态更新（自动判断非null值才进行更新）
            spuMapper.updateById(spu);
            //根据商品的上下架定义不同的Routingkey
            String routingKey = saleable ? MQConstants.RoutingKey.ITEM_UP_KEY : MQConstants.RoutingKey.ITEM_DOWN_KEY;
            //发送消息给MQ
            amqpTemplate.convertAndSend(MQConstants.Exchange.ITEM_EXCHANGE_NAME, routingKey, id);

        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.UPDATE_OPERATION_FAIL);
        }
    }

    // 获取spuDetail表的数据
    public SpuDetail findSpuDetailBySpuId(Long id) {
        SpuDetail spuDetail = spuDetailMapper.selectById(id);
        if (spuDetail == null) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return spuDetail;
    }

    //根据spuId获取所有的Sku表数据
    public List<Sku> findSkusBySpuId(Long id) {
        //1.封装条件
        Sku sku = new Sku();
        sku.setSpuId(id);

        QueryWrapper<Sku> queryWrapper = Wrappers.query(sku);

        //2.执行查询，获取结果
        List<Sku> skus = skuMapper.selectList(queryWrapper);

        //3.处理结果，返回
        if (CollectionUtils.isEmpty(skus)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skus;
    }

    /**
     * 根据spuId查询SpuDTO
     */
    public SpuDTO findSpuById(Long id) {

        try {
            //1.查询spu
            Spu spu = spuMapper.selectById(id);

            //2.拷贝数据
            SpuDTO spuDTO = BeanHelper.copyProperties(spu, SpuDTO.class);
            //3.封装分类名称
            List<Category> categoryList = categoryService.findCategoriesByIds(
                    Arrays.asList(spuDTO.getCid1(), spuDTO.getCid2(), spuDTO.getCid3())
            );

            String categoryName = categoryList.stream().map(Category::getName)
                    .collect(Collectors.joining("/"));

            spuDTO.setCategoryName(categoryName);

            //4.封装品牌名称
            Brand brand = brandService.findBrandById(spuDTO.getBrandId());
            spuDTO.setBrandName(brand.getName());

            //5.封装所有Sku集合
            List<Sku> skus = this.findSkusBySpuId(spuDTO.getId());
            spuDTO.setSkus(skus);

            //6.封装spuDetail对象
            SpuDetail detail = this.findSpuDetailBySpuId(spuDTO.getId());
            spuDTO.setSpuDetail(detail);
            return spuDTO;

        } catch (Exception e) {
            e.printStackTrace();
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
    }

    public List<Sku> findSkusByIds(List<Long> ids) {
        List<Sku> skus = skuMapper.selectBatchIds(ids);
        if (CollectionUtils.isEmpty(skus)) {
            throw new LyException(ExceptionEnum.GOODS_NOT_FOUND);
        }
        return skus;
    }

    public void minusStock(Map<Long, Integer> paramMap) {
        paramMap.entrySet().forEach(entry->{
            Long skuId = entry.getKey();
            Integer num = entry.getValue();

            //查询当前Sku的库存量
            Sku sku = skuMapper.selectById(skuId);
            //扣减库存量
            sku.setStock(sku.getStock()-num);
            //更新到数据库
            skuMapper.updateById(sku);
        });

    }
}
